package lambda;

import beh2param.Apple;
import com.google.common.collect.Lists;
import dto.Book;
import dto.Dish;
import dto.Fruit;
import dto.PersonMan;
import lambda.interfaces.BufferedReaderProcessor;
import lambda.interfaces.Converter;
import lib.streaming.CaloricLevel;
import org.junit.Assert;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

/**
 * @author kxd java8中lambdaTest
 */
public class LambdaTest {
    /**
     * 不使用lambda表达式 排序
     */
    @Test
    public void test1() {
        List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return b.compareTo(a);
            }
        });
    }

    /**
     * 使用lambda表达式排序
     */
    @Test
    public void test2() {
        List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
        Collections.sort(names, (String a, String b) -> {
            return b.compareTo(a);
        });
        System.out.println(names);
    }

    /**
     * 排序
     */
    @Test
    public void test3() {
        List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
        Collections.sort(names, (String a, String b) -> b.compareTo(a));
        System.out.println(names);
    }

    /**
     * 逆向排序
     */
    @Test
    public void test4() {
        List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
        Collections.sort(names, (a, b) -> b.compareTo(a));
        Collections.reverseOrder();
        System.out.println(names);
    }

    @Test
    public void test5() {
        BufferedReaderProcessor p = (BufferedReader br) -> br.readLine();
        Function<BufferedReader, String> f = (BufferedReader reader) -> {
            try {
                return reader.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";
        };
    }


    public void methodReference() {
        List<String> str = Arrays.asList("a", "b", "A", "B");
        str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
        str.sort(String::compareToIgnoreCase);
        Function<String, Integer> stringToInteger = (String s) -> Integer.parseInt(s);
        BiPredicate<List<String>, String> contains = (list, element) -> list.contains(element);
        Function<String, Integer> stringIntegerFunction = Integer::parseInt;
        BiPredicate<List<String>, String> contains1 = List::contains;
        Supplier<Apple> c1 = Apple::new;
        Supplier<Apple> c2 = () -> new Apple();
        Apple a1 = c1.get();
        Function<Integer, Apple> c3 = Apple::new;
        Function<Integer, Apple> c4 = (weight) -> new Apple(weight);
        Apple a3 = c3.apply(110);
    }

    public static void fruitMap() {
        //Comparing
        Map<String, Function<Integer, Function<Integer, Fruit>>> map = new HashMap<>(100);
        Comparator.comparing(Apple::getWeight).reversed();
        //comparing again 两个属性比对
        Comparator.comparing(Apple::getWeight).reversed().thenComparing(Apple::getColor);

        //Predicate
        Predicate<Apple> redApple = (a1) -> a1.getColor().equals("red");
        Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150)
                .or(a -> "green".equals(a.getColor()));
    }


    @Test
    public void test6() {
        //闭包lambda
        Arrays.asList("a", "b", "c").forEach(e -> System.out.println(e));
        Arrays.asList("a", "b", "c").forEach(System.out::println);
        Arrays.asList("a", "b", "c").forEach(e -> {
            System.out.println(e);
            System.out.println(e + "e");
        });

        Arrays.asList("a", "b", "c").forEach(e -> System.out.print(e + ","));

        Arrays.asList("a", "b", "c").sort((e1, e2) -> e1.compareTo(e2));
    }


    @Test
    public void testLambdaInterface() {
        Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
        Integer converted = converter.convert("123");
        System.out.println(converted);
    }

    @Test
    public void testVisitor() {
        final int num = 1;
        Converter<Integer, String> stringConverter = (from -> String.valueOf(from + num));
        stringConverter.convert(2);
    }

    @Test
    public void testVisitor2() {
        int finalNum = 1;
        Converter<Integer, String> stringConverter = (from -> String.valueOf(from + finalNum));
        Assert.assertEquals("3", stringConverter.convert(2));
    }

    @Test
    public void test() {
        List<Dish> menu = new ArrayList<>();
        Dish dish1 = new Dish("dish1", true, 1, Dish.Type.FISH);
        Dish dish2 = new Dish("dish2", false, 1, Dish.Type.OTHER);
        Dish dish3 = new Dish("dish3", true, 1, Dish.Type.OTHER);
        Dish dish4 = new Dish("dish4", false, 1, Dish.Type.FISH);
        menu.add(dish1);
        menu.add(dish2);
        menu.add(dish3);
        menu.add(dish4);
        menu.stream()
                .filter(Dish::isVegetarian)
                .limit(1)
                .collect(toList()).forEach(System.out::println);
        menu.stream()
                .filter(Dish::isVegetarian)
                .collect(toList())
                .forEach(System.out::println);
        menu = menu.stream()
                .filter(d -> (d.getType().equals(Dish.Type.FISH)))
                .collect(toList());

        menu.forEach(s -> System.out.println(s.getName()));
        List<String> words = Arrays.asList("Java8", "Lambdas", "in", "action");
        List<Integer> wordLengths = words.stream().map(String::length)
                .collect(toList());

        List<Integer> dishNameLengths = menu.stream()
                .map(Dish::getName)
                .map(String::length).collect(toList());
        //Arrays::stream()
        //flatMap using flatmap to find the unique characters from  a list of words
        List<String> uniqueCharacters = words.stream()
                .map(w -> w.split("")).flatMap(Arrays::stream)
                .distinct().collect(Collectors.toList());

        //mapping matching least one
        if (menu.stream().anyMatch(Dish::isVegetarian)) {
            System.out.printf("The menu is vegetarian friendly!");
        }
        //matching all <>nonematching

        if (menu.stream().allMatch(Dish::isVegetarian)) {
            System.out.printf("The menu is vegetarian friendly!");
        }

        menu.stream().map(d -> 1).reduce(0, (a, b) -> a + b);

        //sum.min,max,.average()
        //OptionalDouble

        OptionalDouble sumCalories = menu.stream()
                .mapToInt(Dish::getCalories).average();
        IntStream intStream = menu.stream().mapToInt(Dish::getCalories);

        Stream<Integer> stream = intStream.boxed();

        //numeric rangdes  on IntStream and longStream to generate such ranges :range and reangelosed

        IntStream evenNumbers = IntStream.rangeClosed(1, 100).filter(n -> n % 2 == 0);
        System.out.println(evenNumbers.count());
        // stream from string
        //stream from int
        //stream from functions
        //Comparator 的使用
        long howManyDishes = menu.stream().collect(Collectors.counting());
        long howMany = menu.stream().count();

        Comparator<Dish> dishCaloriesComparator = Comparator
                .comparingInt(Dish::getCalories);
        Optional<Dish> mostCalorieDish = menu.stream()
                .collect(maxBy(dishCaloriesComparator));
        //summingInt,summingLong,summingDouble
        int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

        //averagingInt,averagingLong,averagingDouble

        double averageCalories = menu.stream()
                .collect(averagingInt(Dish::getCalories));

        //IntSummaryStatistics
        IntSummaryStatistics menuStatistics = menu.stream()
                .collect(summarizingInt(Dish::getCalories));
        menuStatistics.getAverage();
        menuStatistics.getSum();

        //String shortMenu=menu.stream().collect(joining());

        String shortMenu = menu.stream()
                .map(Dish::getName).collect(joining(","));
        int totalCalories2 = menu.stream()
                .collect(reducing(0, Dish::getCalories, Integer::sum));


        int totalCalories3 = menu.stream()
                .map(Dish::getCalories).reduce(Integer::sum).get();
        int totalCalories4 = menu.stream()
                .mapToInt(Dish::getCalories).sum();
        Map<Dish.Type, List<Dish>> dishByType = menu.stream()
                .collect(groupingBy(Dish::getType));

        Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
                groupingBy(dish -> {
                    if (dish.getCalories() <= 400) {
                        return CaloricLevel.DIET;
                    } else if (dish.getCalories() <= 700) {
                        return CaloricLevel.NORMAL;
                    } else {
                        return CaloricLevel.FAT;
                    }
                })
        );
        //Multilevel grouping

        // menu.stream().collect(groupingBy(Dish::getType,groupingBy(dish->{if(dish.getCalories()<=400){return CaloricLevel.DIET;}else if(dish.getCalories()<=700){return CaloricLevel.FAT;}})));

        //collect in the subgroup
        Map<Dish.Type, Long> typesCount = menu.stream()
                .collect(groupingBy(Dish::getType, counting()));

        //Partitioning

        Map<Boolean, List<Dish>> partitionedMenu = menu.stream()
                .collect(partitioningBy(Dish::isVegetarian));

        //Result:{false=[pork, beef, chicken, prawns, salmon], true=[french fries, rice, season fruit, pizza]}
        List<Dish> vegetarianDishes = partitionedMenu.get(true);
    }

    @Test
    public void testReduce() {

        List<Integer> numbers = new ArrayList<>();
        int sum = 0;
        for (int i : numbers) {
            sum += i;
        }
        //reduce :two arguments   an initial vaule 0;
        sum = numbers.stream()
                .reduce(0, (a, b) -> a + b);
        sum = numbers.stream()
                .reduce(0, Integer::sum);

        //reduce find the max member
        Optional<Integer> max = numbers.stream().reduce(Integer::max);
        //reduce find the min member
        Optional<Integer> min = numbers.stream().reduce(Integer::min);
        numbers.parallelStream().reduce(0, Integer::sum);
    }

    @Test
    public void test9() {
        List<String> list = Arrays.asList("AA", "BB", "CC", "BB", "CC", "AA", "AA");
        long l = list.stream().distinct().count();
        System.out.println("No .of distinct elements:" + l);
        String output = list.stream().distinct().collect(Collectors.joining(","));
        System.out.println(output);
    }

    @Test
    public void test10() {
        List<Book> list = new ArrayList();
        {
            list.add(new Book("Core java", 200));
            list.add(new Book("Core java", 200));
            list.add(new Book("Learning Framework", 300));
            list.add(new Book("Spring mvc", 300));
            list.add(new Book("Spring mvc", 400));
        }
        long l = list.stream().distinct().count();
        System.out.println("No . of distinct books:" + l);
        list.stream().distinct().forEach(b -> System.out.println("书名:" + b.getName() + ",价格" + b.getPrice()));
    }


    @Test
    public void testPeek() {
        List<Integer> numbers = Arrays.asList(2, 3, 4, 5, 6);
        List<Integer> result = numbers.stream()
                .peek(x -> System.out.println("from stream:" + x))
                .map(x -> x + 1)
                .peek(x -> System.out.println("after map:" + x))
                .filter(x -> x % 2 == 0)
                .peek(x -> System.out.println("after filter:" + x))
                .limit(3)
                .peek(x -> System.out.println("after limit:" + x))
                .collect(Collectors.toList());
        Comparator.naturalOrder();
    }

    @Test
    public void testNum11() {

        List<PersonMan> people = Lists.newArrayList();
        people.add(new PersonMan("zhangan", 20));

        people.add(new PersonMan("lisi", 26));
        people.add(new PersonMan("wangwu", 35));
        int sum = people.stream().mapToInt(PersonMan::getAge).sum();
        System.out.println("total of ages" + sum);
    }

    @Test
    public void testBigDecial() {
        List<PersonMan> people = Lists.newArrayList();
        people.add(new PersonMan(20, new BigDecimal(10.5), "zhangan"));
        people.add(new PersonMan(26, new BigDecimal(22.5), "lisi"));
        people.add(new PersonMan(35, new BigDecimal(15.54), "wangwu"));


        BigDecimal sum = people.stream().map(PersonMan::getAmount).reduce(BigDecimal::add).get();
        sum = sum.setScale(1, BigDecimal.ROUND_DOWN);
        System.out.println(sum);
    }
}
